package DyingBaby.backend.common;

import DyingBaby.common.Error;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.LinkedHashMap;

/**
 * AbstractCache 实现了一个引用计数策略的缓存
 * 支持引用计数和LRU两种策略
 */
public abstract class AbstractCache<T> {
    private HashMap<Long, T> cache;                     // 实际缓存的数据
    private HashMap<Long, Integer> references;          // 资源的引用个数
    private HashMap<Long, Boolean> getting;             // 正在被获取的资源

    private int maxResource;                            // 缓存的最大缓存资源数
    private int count = 0;                              // 缓存中元素的个数
    private Lock lock;
    private CacheStrategy strategy;                     // 缓存策略
    
    // LRU相关字段
    private LinkedHashMap<Long, Long> accessOrder;      // 访问顺序记录（LRU策略使用）
    private long evictionCount = 0;                     // 缓存驱逐次数
    
    // 缓存统计信息
    private long hitCount = 0;                          // 缓存命中次数
    private long missCount = 0;                         // 缓存未命中次数
    private long totalRequests = 0;                     // 总请求次数

    public AbstractCache(int maxResource) {
        this(maxResource, CacheStrategy.REFERENCE_COUNT);
    }
    
    public AbstractCache(int maxResource, CacheStrategy strategy) {
        this.maxResource = maxResource;
        this.strategy = strategy;
        cache = new HashMap<>();
        references = new HashMap<>();
        getting = new HashMap<>();
        lock = new ReentrantLock();
        
        // 初始化LRU访问顺序记录
        if (strategy == CacheStrategy.LRU) {
            accessOrder = new LinkedHashMap<Long, Long>(16, 0.75f, true) {
                @Override
                protected boolean removeEldestEntry(java.util.Map.Entry<Long, Long> eldest) {
                    return false; // 手动控制驱逐
                }
            };
        }
    }

    protected T get(long key) throws Exception {
        if (CacheConfig.isStatsEnabled()) {
            totalRequests++; // 增加总请求次数
        }
        
        while(true) {
            lock.lock();
            if(getting.containsKey(key)) {
                // 请求的资源正在被其他线程获取
                lock.unlock();
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    continue;
                }
                continue;
            }

           // 资源在缓存中，直接返回，同时更新资源的引用数加一
            if(cache.containsKey(key)) {
                T obj = cache.get(key);
                references.put(key, references.get(key) + 1);
                
                // 更新LRU访问顺序
                if (strategy == CacheStrategy.LRU) {
                    accessOrder.put(key, System.currentTimeMillis());
                }
                
                if (CacheConfig.isStatsEnabled()) {
                    hitCount++; // 增加命中次数
                }
                lock.unlock();
                return obj;
            }

            // 如果资源不在缓存中，先判断是否已经达到设定的最大缓存资源数
            if(maxResource > 0 && count == maxResource) {
                if (strategy == CacheStrategy.REFERENCE_COUNT) {
                    // 引用计数策略：缓存满时抛出异常
                    lock.unlock();
                    throw Error.CacheFullException;
                } else if (strategy == CacheStrategy.LRU) {
                    // LRU策略：驱逐最久未使用的资源
                    evictLRU();
                }
            }
            if (CacheConfig.isStatsEnabled()) {
                missCount++; // 增加未命中次数
            }
            count++;
            getting.put(key, true);
            lock.unlock();
            break;
        }

        T obj = null;
        try {
//            从数据源获取资源是相对费时的操作
            obj = getForCache(key);
        } catch(Exception e) {
            lock.lock();
            count--;
            getting.remove(key);
            lock.unlock();
            throw e;
        }

        lock.lock();
        getting.remove(key);
        cache.put(key, obj);
        references.put(key, 1);
        
        // 记录LRU访问顺序
        if (strategy == CacheStrategy.LRU) {
            accessOrder.put(key, System.currentTimeMillis());
        }
        
        lock.unlock();
        return obj;
    }

    /**
     * 强行释放一个缓存，如果发现引用数为0，回源并删除缓存中所有相关的结构，
     * 如果不为0，还有引用，只用更新引用数减一即可
     */
    protected void release(long key) {
        lock.lock();
        try {
            int ref = references.get(key)-1;
            if(ref == 0) {
                T obj = cache.get(key);
                releaseForCache(obj);
                references.remove(key);
                cache.remove(key);
                
                // 清理LRU访问顺序记录
                if (strategy == CacheStrategy.LRU) {
                    accessOrder.remove(key);
                }
                
                count--;
            } else {
                references.put(key, ref);
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 关闭缓存，写回所有资源。遍历缓存中的所有key，逐一删除
     */
    protected void close() {
        lock.lock();
        try {
            Set<Long> keys = cache.keySet();
            for (long key : keys) {
                T obj = cache.get(key);
                releaseForCache(obj);
                references.remove(key);
                cache.remove(key);
            }
            
            // 清理LRU访问顺序记录
            if (strategy == CacheStrategy.LRU && accessOrder != null) {
                accessOrder.clear();
            }
        } finally {
            lock.unlock();
        }
    }


    /**
     * 当资源不在缓存时的获取行为
     */
    protected abstract T getForCache(long key) throws Exception;
    /**
     * 当资源被驱逐时的写回行为
     */
    protected abstract void releaseForCache(T obj);
    
    /**
     * 获取缓存命中次数
     */
    public long getHitCount() {
        return hitCount;
    }
    
    /**
     * 获取缓存未命中次数
     */
    public long getMissCount() {
        return missCount;
    }
    
    /**
     * 获取总请求次数
     */
    public long getTotalRequests() {
        return totalRequests;
    }
    
    /**
     * 获取缓存命中率
     */
    public double getHitRate() {
        if (totalRequests == 0) {
            return 0.0;
        }
        return (double) hitCount / totalRequests;
    }
    
    /**
     * 获取缓存统计信息字符串
     */
    public String getCacheStats() {
        if (strategy == CacheStrategy.LRU) {
            return String.format("LRU缓存统计 - 总请求: %d, 命中: %d, 未命中: %d, 驱逐: %d, 命中率: %.2f%%, 当前缓存大小: %d/%d", 
                    totalRequests, hitCount, missCount, evictionCount, getHitRate() * 100, count, maxResource);
        } else {
            return String.format("引用计数缓存统计 - 总请求: %d, 命中: %d, 未命中: %d, 命中率: %.2f%%, 当前缓存大小: %d/%d", 
                    totalRequests, hitCount, missCount, getHitRate() * 100, count, maxResource);
        }
    }
    
    /**
     * 重置缓存统计信息
     */
    public void resetStats() {
        lock.lock();
        try {
            hitCount = 0;
            missCount = 0;
            totalRequests = 0;
            evictionCount = 0;
        } finally {
            lock.unlock();
        }
    }
    
    /**
     * 获取当前缓存元素个数
     */
    protected int getCurrentCount() {
        return count;
    }
    
    /**
     * 获取最大缓存资源数
     */
    protected int getMaxResource() {
        return maxResource;
    }
    
    /**
     * 获取缓存驱逐次数
     */
    public long getEvictionCount() {
        return evictionCount;
    }
    
    /**
     * 获取当前缓存策略
     */
    public CacheStrategy getStrategy() {
        return strategy;
    }
    
    /**
     * LRU策略：驱逐最久未使用的资源
     */
    private void evictLRU() {
        if (strategy == CacheStrategy.LRU && accessOrder != null && !accessOrder.isEmpty()) {
            // 找到最久未使用的key（LinkedHashMap的第一个元素）
            Long keyToEvict = accessOrder.keySet().iterator().next();
            
            // 检查该key是否还有引用
            if (references.containsKey(keyToEvict) && references.get(keyToEvict) == 0) {
                // 没有引用，可以安全驱逐
                T obj = cache.get(keyToEvict);
                releaseForCache(obj);
                cache.remove(keyToEvict);
                references.remove(keyToEvict);
                accessOrder.remove(keyToEvict);
                count--;
                evictionCount++;
            } else {
                // 还有引用，需要找到下一个可以驱逐的key
                for (Long key : accessOrder.keySet()) {
                    if (references.containsKey(key) && references.get(key) == 0) {
                        T obj = cache.get(key);
                        releaseForCache(obj);
                        cache.remove(key);
                        references.remove(key);
                        accessOrder.remove(key);
                        count--;
                        evictionCount++;
                        break;
                    }
                }
            }
        }
    }
}
